package ru.ifmo.network.skype.network;

import org.apache.log4j.Logger;
import ru.ifmo.network.skype.UserDataProvider;
import ru.ifmo.network.skype.network.data.Group;
import ru.ifmo.network.skype.network.data.TCPCommand;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

import static ru.ifmo.network.skype.network.NetworkUtils.readInt;

/**
 * @author avhaliullin
 */
public class TCPServer implements Runnable {
    private static final Logger log = Logger.getLogger(TCPServer.class);

    private UserDataProvider userDataProvider;
    private SecureConnectionManager secureConnectionManager;
    private GroupsController groupsController;
    private ServerSocket serverSocket;

    public TCPServer(UserDataProvider userDataProvider, SecureConnectionManager secureConnectionManager,
                     GroupsController groupsController, int port) throws IOException {
        this.userDataProvider = userDataProvider;
        this.secureConnectionManager = secureConnectionManager;
        this.groupsController = groupsController;
        serverSocket = new ServerSocket(port);
    }

    private void handleTCP(InputStream in, OutputStream out) throws IOException {

        int intCommand = readInt(in);
        TCPCommand command = TCPCommand.getByValue(intCommand);
        if (command == null) {
            return;
        }
        switch (command) {
            case USER_DATA: {
                ObjectOutput output = new ObjectOutputStream(out);
                userDataProvider.getUserData().writeExternal(output);
            }
            break;
            case SECURE: {
                log.info("Secure");
                Channel channel = secureConnectionManager.wrap(new SocketChannel(in, out));
                handleTCP(channel.getInputStream(), channel.getOutputStream());
            }
            break;
            case GROUP: {
                try {
                    log.info("GROUP invocation");
                    Group group = new Group();
                    group.readExternal(new ObjectInputStream(in));
                    groupsController.addGroupInfo(group);
                } catch (ClassNotFoundException e) {
                    throw new AssertionError(e);
                }
            }
        }
    }

    @Override
    public void run() {
        while (!Thread.interrupted()) {
            try {
                final Socket client = serverSocket.accept();
                new Thread(new Runnable() {
                    public void run() {
                        try {
                            handleTCP(client.getInputStream(), client.getOutputStream());
                        } catch (IOException e) {
                            log.error(e);
                        } finally {
                            try {
                                client.close();
                            } catch (IOException e) {
                                //ignore
                            }
                        }
                    }
                }).start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
